//
//  AnimationButton.swift
//  EasePods
//
//  Created by 李然 on 2024/5/8.
//

import UIKit

struct AnimationButtonStateChangeValue<T> {
    let value: T
    let animated: Bool
    let animationDuration: TimeInterval
}

open class AnimationButton: Button {
    
    typealias ControlState = UInt
    
    open var defaultAnimationDuration: TimeInterval = 0.2

    open override var isEnabled: Bool {
        didSet {
            // manually enables / disables user interaction to restore correct state if loading or enabled state are switched separate or together
            if !isEnabled {
                self.isUserInteractionEnabled = false
            } else {
                self.isUserInteractionEnabled = true
            }
            update()
        }
    }
    
    open override var isHighlighted: Bool {
        didSet {
            update()
        }
    }
    
    open override var isSelected: Bool {
        didSet {
            update()
        }
    }
    /// 要为特定的按钮状态定义各种样式，请重写此函数并为特定的状态设置属性(例如setBackgroundColor(uiccolor . bluecolor ()， for: .高亮显示，动画:true))
    open func configureButtonStyles() {}

    open override func makeUI() {
        super.makeUI()
        
        lockAnimatedUpdate = true
        configureButtonStyles()
        update()
        lockAnimatedUpdate = false
    }
    
    open func setScale(_ scale: CGFloat, for state: UIControl.State = .normal, animated: Bool = true, animationDuration: TimeInterval? = nil) {
        buttonScales[state.rawValue] = AnimationButtonStateChangeValue(value: scale, animated: animated, animationDuration: animationDuration ?? defaultAnimationDuration)
        updateScale()
    }
    
    open func setBackgroundColor(_ color: UIColor, for state: UIControl.State = .normal, animated: Bool = true, animationDuration: TimeInterval? = nil) {
        backgroundColors[state.rawValue] = AnimationButtonStateChangeValue(value: color.cgColor, animated: animated, animationDuration: animationDuration ?? defaultAnimationDuration)
        updateBackgroundColor()
    }
    
    open func setBorderWidth(_ width: CGFloat, for state: UIControl.State = .normal, animated: Bool = true, animationDuration: TimeInterval? = nil) {
        borderWidths[state.rawValue] = AnimationButtonStateChangeValue(value: width, animated: animated, animationDuration: animationDuration ?? defaultAnimationDuration)
        updateBorderWidth()
    }
    
    open func setBorderColor(_ color: UIColor, for state: UIControl.State = .normal, animated: Bool = true, animationDuration: TimeInterval? = nil) {
        borderColors[state.rawValue] = AnimationButtonStateChangeValue(value: color.cgColor, animated: animated, animationDuration: animationDuration ?? defaultAnimationDuration)
        updateBorderColor()
    }
    
    /// 用于锁定初始设置的任何动画状态转换
    private var lockAnimatedUpdate: Bool = true
    
    private var sourceLayer: CALayer {
        return (layer.presentation() ?? layer)
    }
    
    private lazy var buttonScales: [ControlState: AnimationButtonStateChangeValue<CGFloat>] = {
        [UIControl.State.normal.rawValue: AnimationButtonStateChangeValue(value: 1.0, animated: true, animationDuration: self.defaultAnimationDuration)]
    }()
    
    private lazy var backgroundColors: [ControlState: AnimationButtonStateChangeValue<CGColor>] = {
        if let color = self.backgroundColor?.cgColor {
            return [UIControl.State.normal.rawValue: AnimationButtonStateChangeValue(value: color, animated: true, animationDuration: self.defaultAnimationDuration)]
        }
        return [:]
    }()

    private lazy var borderColors: [ControlState: AnimationButtonStateChangeValue<CGColor>] = {
        if let color = self.layer.borderColor {
            return [UIControl.State.normal.rawValue: AnimationButtonStateChangeValue(value: color, animated: true, animationDuration: self.defaultAnimationDuration)]
        }
        return [:]
    }()
    
    private lazy var borderWidths: [ControlState: AnimationButtonStateChangeValue<CGFloat>] = {
        [UIControl.State.normal.rawValue: AnimationButtonStateChangeValue(value: self.layer.borderWidth, animated: true, animationDuration: self.defaultAnimationDuration)]
    }()
}

extension AnimationButton {
    
    private func update() {
        updateScale()
        updateBackgroundColor()
        updateBorderColor()
        updateBorderWidth()
    }
    
    private func updateScale() {
        if let stateChange = buttonScales[state.rawValue] ?? buttonScales[UIControl.State.normal.rawValue], transform.a != stateChange.value, transform.b != stateChange.value {
            let animations = {
                self.transform = CGAffineTransform(scaleX: stateChange.value, y: stateChange.value)
            }
            if stateChange.animated && !lockAnimatedUpdate {
                UIView.animate(withDuration: stateChange.animationDuration, animations: animations)
            } else {
                animations()
            }
        }
    }
    
    private func updateBackgroundColor() {
        if let stateChange = backgroundColors[state.rawValue] ?? backgroundColors[UIControl.State.normal.rawValue], layer.backgroundColor == nil || UIColor(cgColor: layer.backgroundColor!) != UIColor(cgColor: stateChange.value) {
            if stateChange.animated, !lockAnimatedUpdate {
                animate(layer: layer, from: sourceLayer.backgroundColor, to: stateChange.value, forKey: "backgroundColor", duration: stateChange.animationDuration)
            }
            layer.backgroundColor = stateChange.value
        }
    }
    
    private func updateBorderColor() {
        if let stateChange = borderColors[state.rawValue] ?? borderColors[UIControl.State.normal.rawValue], layer.borderColor == nil || UIColor(cgColor: layer.borderColor!) != UIColor(cgColor: stateChange.value) {
            if stateChange.animated, !lockAnimatedUpdate {
                animate(layer: layer, from: sourceLayer.borderColor, to: stateChange.value, forKey: "borderColor", duration: stateChange.animationDuration)
            }
            layer.borderColor = stateChange.value
        }
    }
    
    private func updateBorderWidth() {
        if let stateChange = borderWidths[state.rawValue] ?? borderWidths[UIControl.State.normal.rawValue], stateChange.value != layer.borderWidth {
            if stateChange.animated, !lockAnimatedUpdate {
                animate(layer: layer, from: sourceLayer.borderWidth as AnyObject?, to: stateChange.value as AnyObject, forKey: "borderWidth", duration: stateChange.animationDuration)
            }
            layer.borderWidth = stateChange.value
        }
    }
    
    private func animate(layer: CALayer, from: AnyObject?, to: AnyObject, forKey key: String, duration: TimeInterval) {
        let animation = CABasicAnimation()
        animation.fromValue = from
        animation.toValue = to
        animation.duration = duration
        layer.add(animation, forKey: key)
    }
}
